﻿using ErrorOr;
using Gnivc.CarDealership.Api.Application.Abstractions;
using Gnivc.CarDealership.Api.Application.Abstractions.Repositories;
using Gnivc.CarDealership.Api.Domain.Entities;
using Gnivc.CarDealership.Api.Domain.Enums;
using Gnivc.CarDealership.Api.Domain.ValueObjects;
using MediatR;

namespace Gnivc.CarDealership.Api.Application.Commands.Vehicles.CreateVehicle;

public class CreateVehicleCommandHandler
    : IRequestHandler<CreateVehicleCommand, ErrorOr<CreateVehicleResult>>
{
    private readonly IModelRepository _modelRepository;
    private readonly IVehicleRepository _vehicleRepository;
    private readonly IPriceRepository _priceRepository;
    private readonly IUnitOfWork _unitOfWork;

    public CreateVehicleCommandHandler(
        IModelRepository modelRepository,
        IVehicleRepository vehicleRepository,
        IPriceRepository priceRepository,
        IUnitOfWork unitOfWork)
    {
        _modelRepository = modelRepository;
        _vehicleRepository = vehicleRepository;
        _priceRepository = priceRepository;
        _unitOfWork = unitOfWork;
    }

    public async Task<ErrorOr<CreateVehicleResult>> Handle(
        CreateVehicleCommand command, 
        CancellationToken cancellationToken)
    {
        var getModelResult = await _modelRepository.GetByIdAsync(command.ModelId);

        if (getModelResult.IsError)
            return getModelResult.Errors;

        var createEngineResult = Engine.Create(
            command.EngineVolume, 
            command.EnginePower, 
            (EngineType)command.EngineType);

        if(createEngineResult.IsError)
            return createEngineResult.Errors;

        var model = getModelResult.Value!;
        var engine = createEngineResult.Value!;

        var vehicle = new Vehicle(
            Guid.NewGuid(),
            model,
            command.ReleaseYear,
            command.Mileage,
            engine);

        var price = new Price(
            Guid.NewGuid(), 
            command.Price, 
            vehicle);

        await _unitOfWork.BeginTransactionAsync();

        try
        {
            await _vehicleRepository.AddAsync(vehicle);
            await _priceRepository.AddAsync(price);
            await _unitOfWork.CommitTransactionAsync();
        }
        catch (Exception)
        {
            await _unitOfWork.RollbackTransactionAsync();
            return Error.Failure();
        }

        return new CreateVehicleResult(vehicle.Id);
    }
}
